GO
这篇笔记是关于Shell脚本的调试知识,掌握了Shell脚本的调试技巧,可以让我们在开发大型脚本时做到事半功倍。虽然掌握了Shell脚本的调试技巧很重要,但是如果能够掌握并养成良好的Shell脚本开发的规范和习惯,就可以从源头上降低开发脚本的错误率,从而降低脚本调试的难度和时间,达到未雨绸缪的效果。
1. 常见Shell脚本错误范例
1.1. if条件语句缺少结尾关键字
范例代码:
12345[root@theshu ~]# cat if.shif [ 10 -lt 12 ]thenecho "Yes, 10 is less than 12."执行结果如下:
12[root@theshu ~]# sh if.shif.sh: line 5: syntax error: unexpected end of file
结果给出了提示,第5行存在语法错误:这不是所期待的(意外的)文件结尾。根据这个提示,我们知道脚本的尾部有问题,仔细观察发现,原来是缺少了fi结尾。
说明;
- 在Shell脚本开发中,脚本缺少fi关键字是很常见的问题。另外,当执行脚本时提示输出错误后,不要只看那些提示的错误行,而是要观察整个相关的代码段。
- Shell脚本解释器一般不会对脚本错误进行精确的定位,而是在试图结束一个语句时进行错误统计,因此,掌握语法并养成良好的规范和习惯就显得很重要。
1.2. 循环语句缺少关键字
for、while、until和case语句中的错误是指实际语句段不正确,也许是漏写或拼错了固定结构中的一个保留字。
范例:循环结构语句中缺少关键字引起的错误。
脚本内容
123456789101112while truedostatus=`curl -I -s --connect-timeout 10 $1 | awk '{print $2}'`ok=`curl -I -s --connect-timeout 10 | head -1 | cut -d " " -f 3`if [ "$status" = "200" ] && [ "$ok" = "ok" ]; theecho "this yrl is good"elseecho "this url is bad"fisleep 3done执行上述脚本的结果:
12while.sh: line 8: syntax error near unexpected token `else'while.sh: line 8: ` else'通过提示可知吗,是第8行的语法错误,在
else
附近。经过前后观察可以发现,the
少加了个n
,应为then
。- 如果报错的是循环或条件独立的语句,对上下关联部分语句也都要看一下。
1.3. 成对的符号落了单
成对的符号有[]
、()
、{}
、""
、''
等,如果它们落了单,也会导致一些错误。但是這些错误通过错误提示即可很快排除。
1.4. 中括号两端没空格
中括号两端没空格导致的错误
代码如下:
123456789a=3b=1if [$a -lt $b]thenecho "Yes,$a < $b"elseecho "Yes,$a >= $b"fi执行结果如下:
12zhongkuohao.sh: line 4: [3: command not foundYes,3 >= 1
1.5. Shell语法调试小结
Shell的语法调试并不是很智能,报错也不是很精准,因此就需要我们在开发规范和书写脚本上多下功夫,企业里的Shell脚本大多都是比较短的,因此,开发起来也相对轻松。
如果能在开发过程中,重视书写习惯、开发规范和开发制度,那么就会减少脚本调试的难度和次数,提升开发效率。此外,要对Shell的基本语法十分熟练,这样才能更好地利用脚本调试。
此外,写脚本的思路要清晰,否则将给调试带来困难。可采用的思路如下:
- 首先思考开发框架,尽量模块化开发,复杂的脚本要简单化、分段实现。并采用打游戏过关的思想(第一关、第二关、第三关、直到通关)去完善框架结构。
然后利用函数分模块开发,语法结构如下:
1234函数1()函数2()main()main $* #<==执行主函数需要注意的是,不要强制模块化,分块要合理。
2. Shell脚本调试技巧
2.1. 使用dos2unix命令处理在Windows下开发的脚本
因为Windows系统下的文本文件和Linux系统下的文本文件在一些符号上面有差别(比如结尾换行符),所以,在Windows下编辑的Shell脚本直接放在Linux下执行会出现错误,所以需要用dos2unix
工具来转换一下文件。
格式:dos2unix filename.sh
如果没有dos2unix
,则用下面的命令进行安装:yum install dos2unix -y
2.2. 使用echo命令调试
echo
命令是最有用的调试脚本的工具之一。一般应在可能出现问题的脚本的重要部分加入echo命令,例如在变量读取或修改操作的前后加入echo命令,并紧挨着退出命令exit。
利用echo调试一个简单的判断脚本:
提示:这个调试方法不是Shell的专利,PHP、ASP、Perl、Python等语言都可以使用这样简单又好用的调试方法。
2.3. 使用bash命令参数调试
可以利用bash
或sh
本身自带的参数进行调试。格式为:sh [-nvx] script.sh
参数 | 说明 |
---|---|
-n | 不会执行该脚本,仅查询脚本语法是否有问题,并给出错误提示 |
-v | 在执行脚本时,先将脚本的内容输出到屏幕上,然后执行脚本,如果有错误,也会给出错误提示 |
-x | 将执行的脚本内容及输出显示到屏幕上,这是对调试很有用的参数 |
说明:这些参数同样适用于bash
2.3.1. sh参数-n的测试
|
|
2.3.2. sh参数-v的测试
普通的错误脚本的执行结果如下:
123456[root@theshu ~]# sh -v script.shecho "Hello $USER,echo "Today is $(date +%F)"script.sh: line 3: unexpected EOF while looking for matching `"'script.sh: line 4: syntax error: unexpected end of file带函数的错误脚本的执行结果如下:
123456789101112131415[root@theshu ~]# sh -v c.shtheshu(){echo "I am theshu!"}theshan(){echo "I am theshan "}theshu1c.sh: line 11: theshu1: command not foundtheshanI am theshan
2.3.3. sh参数-x的测试
跟踪script.sh脚本的执行过程:
1234[root@theshu ~]# cat script.shecho "Hello $USER,"echo "Today is $(date +%F)"执行结果如下:
123456[root@theshu ~]# sh -x script.sh+ echo 'Hello root,'Hello root,++ date +%F+ echo 'Today is 2018-03-01'Today is 2018-03-01
说明:
- 使用
-x
追踪脚本是一种非常好的方法,它可以在执行前列出所执行的所有程序段 - 如果是程序段落,则在输出时,最前面会加上
+
符号,表示它是程序代码 - 一般情况下如果是调试逻辑错误的脚本,用
-x
的效果更佳 - 缺点是:加载系统函数库等很多我们不想看其整个过程的脚本时,会有太多输出,导致很难查看所需的内容
- 利用
sh -x filename.sh
调试的缺点可以用set -x
命令来弥补,它可以缩小调试的作用域 在一个比较长的脚本中,你会看到很多的执行跟踪的输出,有时候阅读起来非常费劲,此时,可以在每一行的前面内容修改一下提示符,这会非常有用。要做到这样,只需要设置下面的环境变量:
12345# set | grep PS[1-5]PS1='[\u@\h \W]\$ 'PS2='> 'PS4='+ '#<==只需修改PS4变量即可,在默认情况下表示加号参数
-x
是一个不可多得的参数,在生产环境中,经常会通过参数-x
来实现调试的目的。一般情况下,如果执行脚本发生问题(非语法问题),利用-x
参数,就可以知道问题出在哪一行。
2.4. 使用set命令调试部分脚本内容
set
命令也可以用于辅助脚本调试。以下是set命令常用的调试选项。
选项 | 说明 |
---|---|
set -n |
读命令但并不执行 |
set -v |
显示读取的所有行 |
set -x |
显示所有命令及其参数 |
提示:通过
set -x
命令开启调试功能,而通过set +x
关闭调试功能。
set命令的最大优点是,和bash -x
相比,set -x
可以缩小调试的作用域。
范例:调试打印九九乘法表的简版脚本:
脚本内容:
123456789101112[root@theshu ~]# cat 99dug.shset -x #<==表示从这里开启脚本调试for a in `seq 9`dofor b in `seq 9`do[ $a -ge $b ] && echo -en "$a x $b = $(expr $a \* $b) "doneset +x #<==表示到这里结束脚本调试echo " "done执行脚本查看调试输出结果:
12345678910111213141516171819202122232425262728293031323334[root@theshu ~]# sh 99dug.sh++ seq 9+ for a in '`seq 9`'++ seq 9+ for b in '`seq 9`'+ '[' 1 -ge 1 ']'++ expr 1 '*' 1+ echo -en '1 x 1 = 1 '1 x 1 = 1 + for b in '`seq 9`'+ '[' 1 -ge 2 ']'+ for b in '`seq 9`'+ '[' 1 -ge 3 ']'+ for b in '`seq 9`'+ '[' 1 -ge 4 ']'+ for b in '`seq 9`'+ '[' 1 -ge 5 ']'+ for b in '`seq 9`'+ '[' 1 -ge 6 ']'+ for b in '`seq 9`'+ '[' 1 -ge 7 ']'+ for b in '`seq 9`'+ '[' 1 -ge 8 ']'+ for b in '`seq 9`'+ '[' 1 -ge 9 ']'+ set +x2 x 1 = 2 2 x 2 = 43 x 1 = 3 3 x 2 = 6 3 x 3 = 94 x 1 = 4 4 x 2 = 8 4 x 3 = 12 4 x 4 = 165 x 1 = 5 5 x 2 = 10 5 x 3 = 15 5 x 4 = 20 5 x 5 = 256 x 1 = 6 6 x 2 = 12 6 x 3 = 18 6 x 4 = 24 6 x 5 = 30 6 x 6 = 367 x 1 = 7 7 x 2 = 14 7 x 3 = 21 7 x 4 = 28 7 x 5 = 35 7 x 6 = 42 7 x 7 = 498 x 1 = 8 8 x 2 = 16 8 x 3 = 24 8 x 4 = 32 8 x 5 = 40 8 x 6 = 48 8 x 7 = 56 8 x 8 = 649 x 1 = 9 9 x 2 = 18 9 x 3 = 27 9 x 4 = 36 9 x 5 = 45 9 x 6 = 54 9 x 7 = 63 9 x 8 = 72 9 x 9 = 81
提示:加了
set -x
,在运行脚本的时候,就不要使用sh -x
了。
熟悉sh及set的用法,可以让我们能够得心应手地管理Linux下Shell脚本执行的过程。在Shell脚本的学习上,需要“多看、多模仿,并将已有脚本修改成自己需要的样式”,这是最快的学习手段。网络上有很多实用的脚本,作为初学者,可以先将这些脚本拿过来,将其改成为适合自己服务器的脚本,然后再慢慢熟悉,慢慢尝试开发新的脚本。这种先模仿后开发的学习方法,会让你的学习事半功倍,而且学习起来也会很有兴趣和成就感。
Linux系统上本来就有很多适合阅读的规范脚本,如果想要更加深入地掌握Shell脚本编程,最好的方法就是阅读系统的这些脚本,然后仔细研究每一行都是什么作用,为什么这样写,久而久之,你的Shell开发水平就会得到大幅度提高。
2.5. 其它调试Shell脚本的工具
下面再引荐两款Shell的调试工具吗,可以作为扩展知识来研究一下。
- Shell调试工具:
bashdb
:它是一个类似于GDB的调试工具,可以完成对Shell脚本的断点设置、单步执行、变量观察等许多功能。不过一般很少有人使用它。 - Shell调试工具:
shellcheck
:它是一个可检查sh/bash脚本和命令语法的小工具。一般也是很少有人使用它。
3. 小结
这篇学习笔记主要介绍了Shell的调试技巧,包括:
- 要记得首先用
dos2unix
对脚本(从其它地方拿来用的)进行格式化。 - 执行脚本根据报错来调试时,要知道有时所报错误会不准确,应多关联上下文查看。
- 可通过
sh -x
命令调试整个脚本,且显示执行过程。 set -x
和set +x
命令用于调试部分脚本的执行过程(可在脚本中设置)。- 可通过
echo
命令输出脚本中要确认的变量及相关内容,然后紧跟着使用exit
退出,不执行后面程序,这种方式便于一步步跟踪脚本,对于逻辑错误的调试比较好用。写法即:echo $var; exit
- 最关键的还是要语法熟练,养成良好的编码习惯,提高编程思想,将错误扼杀在萌芽状态之中,从而降低错误率,减轻调试的负担,提高开发效率。
OK